using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace SqlTools.CodeGenerator.Data
{
	public class Table
	{
		protected string _dataDirectory;
		protected string _businessDirectory;
		protected string _sqlDirectory;
		protected string _dataNamespace;
		protected string _businessNamespace;
		protected string _tableName;
		protected string _businessObjectName;
		protected string _dbObjectName;
		protected string _primaryKeyFieldName;
		protected string _primaryKeyClassName;
		protected Table _baseTable;
		protected List<Table> _inheritedTables;
		protected TablePropertyCollection _properties;
		protected MethodCollection _methods;
		protected Project _project;

		protected Method _selectByPK = null;

		protected TableLoadResolution _tempLoadResolution;


		public Table(Project project) : this(project, "", "") { }

		public Table(Project project, string tableName)
			: this(project, tableName, tableName) { }

		public Table(Project project, string tableName, string objectName)
		{
			_dataDirectory = "";
			_businessDirectory = "";
			_sqlDirectory = "";

			_dataNamespace = "";
			_businessNamespace = "";

			_tableName = tableName;
			_businessObjectName = objectName;
			_dbObjectName = "Db" + objectName;

			_primaryKeyFieldName = "";
			_primaryKeyClassName = "";

			_baseTable = null;
			_inheritedTables = new List<Table>();

			_properties = new TablePropertyCollection(this);
			_methods = new MethodCollection();
			_project = project;
		}

		public string DataDirectory
		{
			get { return _dataDirectory; }
			set { _dataDirectory = value; }
		}

		public string BusinessDirectory
		{
			get { return _businessDirectory; }
			set { _businessDirectory = value; }
		}

		public string SQLDirectory
		{
			get { return _sqlDirectory; }
			set { _sqlDirectory = value; }
		}

		public string DataNamespace
		{
			get { return _dataNamespace; }
			set { _dataNamespace = value; }
		}

		public string BusinessNamespace
		{
			get { return _businessNamespace; }
			set { _businessNamespace = value; }
		}

		public string TableName
		{
			get { return _tableName; }
			set { _tableName = value; }
		}

		public string BusinessObjectName
		{
			get { return _businessObjectName; }
			set { _businessObjectName = value; }
		}

		public string DbObjectName
		{
			get { return _dbObjectName; }
			set { _dbObjectName = value; }
		}

		public string PrimaryKeyFieldName
		{
			get { return _primaryKeyFieldName; }
			set { _primaryKeyFieldName = value; }
		}

		public string PrimaryKeyClassName
		{
			get { return _primaryKeyClassName; }
			set { _primaryKeyClassName = value; }
		}

		public Table BaseTable
		{
			get { return _baseTable; }
			set
			{
				// remove old back reference
				if ( _baseTable != null && _baseTable != value )
				{
					_baseTable.InheritedTables.Remove(this);
				}

				_baseTable = value;

				// add back reference
				if ( _baseTable != null && !_baseTable.InheritedTables.Contains(this) )
				{
					_baseTable.InheritedTables.Add(this);
					// now remove the foreign key
					RemoveBaseTableForeignKey();
				}
			}
		}

		public List<Table> InheritedTables
		{
			get { return _inheritedTables; }
		}

		public TablePropertyCollection Properties
		{
			get { return _properties; }
		}

		public MethodCollection Methods
		{
			get { return _methods; }
		}

		public Method SelectByPK
		{
			get { return _selectByPK; }
			set { _selectByPK = value; }
		}

		public Project Project
		{
			get { return _project; }
		}

		public PrimaryKeyTableProperty GetPrimaryKey()
		{
			foreach ( TableProperty prop in _properties )
			{
				if ( prop.Type == TablePropertyType.PrimaryKey )
				{
					return prop as PrimaryKeyTableProperty;
				}
			}

			return null;
		}

		#region Save
		public virtual void SaveXml(XmlDocument doc, XmlElement parent)
		{
			XmlElement xmlTable = CreateXmlElement(doc);
			parent.AppendChild(xmlTable);

			// create properties
			CreatePropertiesXml(doc, xmlTable);

			// create methods
			CreateMethodsXml(doc, xmlTable);
		}

		protected virtual XmlElement CreateXmlElement(XmlDocument doc)
		{
			XmlElement xmlTable = doc.CreateElement("Table");

			xmlTable.Attributes.Append(doc.CreateAttribute("DataDirectory")).Value = _dataDirectory;
			xmlTable.Attributes.Append(doc.CreateAttribute("BusinessDirectory")).Value = _businessDirectory;
			xmlTable.Attributes.Append(doc.CreateAttribute("SqlDirectory")).Value = _sqlDirectory;
			xmlTable.Attributes.Append(doc.CreateAttribute("DataNamespace")).Value = _dataNamespace;
			xmlTable.Attributes.Append(doc.CreateAttribute("BusinessNamespace")).Value = _businessNamespace;
			xmlTable.Attributes.Append(doc.CreateAttribute("TableName")).Value = _tableName;
			xmlTable.Attributes.Append(doc.CreateAttribute("BusinessObjectName")).Value = _businessObjectName;
			xmlTable.Attributes.Append(doc.CreateAttribute("DbObjectName")).Value = _dbObjectName;
			xmlTable.Attributes.Append(doc.CreateAttribute("PrimaryKeyFieldName")).Value = _primaryKeyFieldName;
			xmlTable.Attributes.Append(doc.CreateAttribute("PrimaryKeyClassName")).Value = _primaryKeyClassName;
			if ( _baseTable != null )
				xmlTable.Attributes.Append(doc.CreateAttribute("BaseTable")).Value = _baseTable.TableName;

			return xmlTable;
		}

		protected void CreatePropertiesXml(XmlDocument doc, XmlElement parent)
		{
			XmlElement xmlProperties = doc.CreateElement("Properties");
			parent.AppendChild(xmlProperties);

			foreach ( TableProperty prop in _properties )
			{
				prop.SaveXml(doc, xmlProperties);
			}
		}

		protected void CreateMethodsXml(XmlDocument doc, XmlElement parent)
		{
			XmlElement xmlMethods = doc.CreateElement("Methods");
			parent.AppendChild(xmlMethods);

			foreach ( Method method in _methods )
			{
				method.SaveXml(doc, xmlMethods);
			}
		}
		#endregion

		#region Load
		public virtual void LoadXml(XmlElement xmlTable)
		{
			_dataDirectory = xmlTable.GetAttribute("DataDirectory");
			_businessDirectory = xmlTable.GetAttribute("BusinessDirectory");
			_sqlDirectory = xmlTable.GetAttribute("SqlDirectory");
			_dataNamespace = xmlTable.GetAttribute("DataNamespace");
			_businessNamespace = xmlTable.GetAttribute("BusinessNamespace");
			_tableName = xmlTable.GetAttribute("TableName");
			_businessObjectName = xmlTable.GetAttribute("BusinessObjectName");
			_dbObjectName = xmlTable.GetAttribute("DbObjectName");
			_primaryKeyFieldName = xmlTable.GetAttribute("PrimaryKeyFieldName");
			_primaryKeyClassName = xmlTable.GetAttribute("PrimaryKeyClassName");

			string baseTable = "";

			baseTable = xmlTable.GetAttribute("BaseTable");

			if ( !string.IsNullOrEmpty(baseTable) )
			{
				_tempLoadResolution._baseTable = baseTable;
			}
			else
			{
				_tempLoadResolution._baseTable = "";
			}

			XmlNodeList nodes = xmlTable.GetElementsByTagName("Properties");
			if ( nodes.Count > 0 )
			{
				_properties.LoadXml(nodes[0] as XmlElement);
			}


			nodes = xmlTable.GetElementsByTagName("Methods");
			if ( nodes.Count > 0 )
			{
				LoadMethodsXml(nodes[0] as XmlElement);
			}
		}

		private void LoadMethodsXml(XmlElement xmlElement)
		{
			foreach ( XmlNode node in xmlElement.ChildNodes )
			{
				if ( node.NodeType == XmlNodeType.Element )
				{
					XmlElement xmlMethod = node as XmlElement;

					Method method = new Method("", this, MethodType.Custom);

					method.LoadXml(xmlMethod);

					_methods.Add(method);
				}
			}
		}
		#endregion

		public override string ToString()
		{
			return _tableName;
		}

		public bool IsPrimaryKeyProperty(TableProperty prop)
		{
			foreach ( TableProperty tp in _properties )
			{
				if ( tp.Type == TablePropertyType.PrimaryKey )
				{
					PrimaryKeyTableProperty pktp = tp as PrimaryKeyTableProperty;

					foreach ( TableProperty stp in pktp.Properties )
					{
						if ( stp == prop )
							return true;
					}
				}
				else
				{
					if ( tp == prop )
						return false;
				}
			}

			return false;
		}

		public void LoadResolve()
		{
			if ( !string.IsNullOrEmpty(_tempLoadResolution._baseTable) )
			{
				Table table = _project.Tables[_tempLoadResolution._baseTable];

				// set through property instead of field so that
				// the back-reference is created properly
				BaseTable = table;
			}
		}

		public void RemoveBaseTableForeignKey()
		{
			bool isPKProp = false;

			ForeignKeyTableProperty fktp = null;

			foreach ( TableProperty tp in _properties )
			{
				if ( tp.Type == TablePropertyType.PrimaryKey )
				{
					PrimaryKeyTableProperty pktp = tp as PrimaryKeyTableProperty;

					foreach ( TableProperty stp in pktp.Properties )
					{
						if ( stp.Type == TablePropertyType.ForeignKey )
						{
							ForeignKeyTableProperty fk = stp as ForeignKeyTableProperty;

							if ( fk.ReferenceTable == _baseTable )
							{
								fktp = fk;
								isPKProp = true;
								break;
							}
						}
					}
				}
				else if ( tp.Type == TablePropertyType.ForeignKey )
				{
					ForeignKeyTableProperty fk = tp as ForeignKeyTableProperty;

					if ( fk.ReferenceTable == _baseTable )
					{
						fktp = fk;
					}
				}

				if ( fktp != null )
					break;
			}

			if ( fktp != null )
			{
				// remove from local properties
				if ( isPKProp )
				{
					// if its a primary key property
					// we will do nothing, as this 
					// may have negative that haven't 
					// been investigated yet
				}
				else
				{
					int index = _properties.IndexOf(fktp);

					_properties.Remove(fktp);
					
					IntrinsicTableProperty itp = fktp.ConvertToIntrinsic();
					itp.BaseClassPK = true;

					_properties.Insert(index, itp);
				}

				// now remove the back reference
				TableProperty tp = fktp.ReferenceTable.Properties.FindPropertyByColumnName(fktp.ReferenceColumnName);

				if ( tp != null && tp.Type == TablePropertyType.Intrinsic )
				{
					IntrinsicTableProperty itp = tp as IntrinsicTableProperty;

					itp.ForeignKeys.Remove(fktp);
				}

			}
		}

		protected struct TableLoadResolution
		{
			public string _baseTable;
		}

	}
}
